package com.fan.play.media.capture.video

import android.Manifest
import android.content.Context
import android.graphics.ImageFormat
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.params.OutputConfiguration
import android.hardware.camera2.params.SessionConfiguration
import android.media.ImageReader
import android.os.Handler
import android.os.HandlerThread
import android.util.Size
import android.view.Surface
import com.fan.play.media.capture.ICapture
import com.fan.play.media.capture.IVideoCallback
import com.fan.play.media.common.MediaDefine
import com.fan.play.utils.AppUtil
import com.fan.play.utils.LogUtil
import java.util.concurrent.locks.ReentrantLock

/**
 * 使用Camera2采集相机画面
 * Camera2使用流程
 * 1.获取CameraManager context.getSystemService(Context.CAMERA_SERVICE)
 * 2.检测是否有权限 context.checkSelfPermission(Manifest.permission.CAMERA)
 * 3.打开相机 cameraManager.openCamera(cameraID)
 * 4.创建CaptureRequest、CameraCaptureSession
 * 5.创建输出接受的Surface SurfaceView/TextureView/ImageRender
 */
class Camera2Capture(
    var mCameraID: Int,
    var mWidth: Int,
    var mHeight: Int,
    var mVideoCallback: IVideoCallback
) : ICapture {

    private var mCameraFacing = CameraCharacteristics.LENS_FACING_BACK

    //摄像头方向
    private var mSensorOrientation = 0

    //手机方向
    private var mDisplayRotation = 0

    private var mCameraDevice: CameraDevice? = null
    private var mCaptureRequest: CaptureRequest.Builder? = null
    private var mCaptureSession: CameraCaptureSession? = null

    private lateinit var mCameraThread: HandlerThread
    private var mCameraHandler: Handler? = null

    //yuv图像接受类
    private var mImageReader: ImageReader? = null
    private var mMaxBuffer = 2
    private var mImageListener = object : ImageReader.OnImageAvailableListener {
        private var isInit = false
        private var lock = ReentrantLock()
        private lateinit var yBuf: ByteArray
        private lateinit var uBuf: ByteArray
        private lateinit var vBuf: ByteArray

        override fun onImageAvailable(reader: ImageReader) {
            val image = reader.acquireLatestImage() ?: return
            try {
                lock.lock()
                if (image.format == ImageFormat.YUV_420_888) {
                    val size = image.width * image.height
                    if (!isInit) {
                        yBuf = ByteArray(size)
                        uBuf = ByteArray(size / 4)
                        vBuf = ByteArray(size / 4)
                        isInit = true
                    }

                    image.planes[0].buffer.get(yBuf, 0, size)
                    for (i in 0 until size / 4) {
                        uBuf[i] = image.planes[1].buffer[i * 2]
                        vBuf[i] = image.planes[2].buffer[i * 2]
                    }

                    //TODO rotation isMirror默认 后续要改
                    mVideoCallback.onVideoFrame(yBuf, uBuf, vBuf, MediaDefine.MediaRawType_I420, 0, false)
                }
                lock.unlock()
            } catch (e: Exception) {
                LogUtil.logError("onImageAvailable error.")
            }
            image.close()
        }
    }

    private var mCameraListener = object : CameraDevice.StateCallback() {
        override fun onOpened(camera: CameraDevice) {
            mCameraDevice = camera
            createSession(camera)
        }

        override fun onDisconnected(camera: CameraDevice) {
            LogUtil.logInfo("onDisconnected")
            stop()
        }

        override fun onError(camera: CameraDevice, error: Int) {
            LogUtil.logError("onError error:$error")
            stop()
        }

    }

    private var mSessionCallback = object : CameraCaptureSession.StateCallback() {
        override fun onConfigured(session: CameraCaptureSession) {
            mCaptureSession = session
            session.setRepeatingRequest(mCaptureRequest!!.build(), null, mCameraHandler)
        }

        override fun onConfigureFailed(session: CameraCaptureSession) {
            LogUtil.logError("onConfigureFailed")
        }
    }

    init {
        init()
    }

    override fun start(): Boolean {
        LogUtil.logInfo("start camera2 capture")
        //检测Camera2的权限
        if (!AppUtil.checkPermission(Manifest.permission.CAMERA)) {
            LogUtil.logInfo("permission denied. " + Manifest.permission.CAMERA)
            return false
        }

        //接受图像的画面
        mImageReader = ImageReader.newInstance(mWidth, mHeight, ImageFormat.YUV_420_888, mMaxBuffer)
        mImageReader?.setOnImageAvailableListener(mImageListener, mCameraHandler)

        val cameraManager = AppUtil.getService<CameraManager>(Context.CAMERA_SERVICE)
        //打开相机 第二个参数能监听相机的状态：CameraDevice.StateCallback
        cameraManager.openCamera(mCameraID.toString(), mCameraListener, mCameraHandler)
        return true
    }

    override fun stop() {
        LogUtil.logInfo("stop camera2 capture")
        mCaptureSession?.let {
            it.stopRepeating()
            it.close()
        }

        mCameraDevice?.close()

        release()
    }

    private fun init() {
        val cameraManager = AppUtil.getService<CameraManager>(Context.CAMERA_SERVICE)
        val characteristics = cameraManager.getCameraCharacteristics(mCameraID.toString())
        characteristics.get(CameraCharacteristics.LENS_FACING)?.let {
            mCameraFacing = it
        }

        val level = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
        if (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
            LogUtil.logInfo("相机硬件不之支持新特性")
        }

        characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)?.let {
            mSensorOrientation = it
        }

        //获取StreamConfigurationMap,它是管理摄像头支持的输出格式和尺寸
        characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)?.let { config ->
            val previewSizeList = config.getOutputSizes(ImageFormat.YUV_420_888)
            val exchange = exchangeWidthAndHeight(mDisplayRotation, mSensorOrientation)

            val previewSize = if (exchange) {
                getBestSize(mHeight, mWidth, previewSizeList)
            } else {
                getBestSize(mWidth, mHeight, previewSizeList)
            }
            mWidth = previewSize.width
            mHeight = previewSize.height
        }
    }

    private fun release() {
        mImageReader?.close()
        mImageReader = null

        mCameraThread.quitSafely()
    }

    /**
     * 创建采集回话
     * @param device 相机设备
     */
    private fun createSession(device: CameraDevice) {
        device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
            mCaptureRequest = this
            addTarget(mImageReader!!.surface)

            //自动对焦
            set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
            //打开闪光灯
            set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)

            val surfaceList = mutableListOf(OutputConfiguration(mImageReader!!.surface))
            val sessionConfig = SessionConfiguration(
                SessionConfiguration.SESSION_REGULAR,
                surfaceList,
                { command -> command?.run() },
                mSessionCallback
            )
            device.createCaptureSession(sessionConfig)
        }
    }

    /**
     * 根据提供的屏幕方向[displayRotation]和相机方向[sensorOrientation] 返回是否需要交换宽高
     */
    private fun exchangeWidthAndHeight(displayRotation: Int, sensorOrientation: Int): Boolean {
        var exchange = false
        when (displayRotation) {
            Surface.ROTATION_0, Surface.ROTATION_180 ->
                if (sensorOrientation == 90 || sensorOrientation == 270)
                    exchange = true

            Surface.ROTATION_90, Surface.ROTATION_270 ->
                if (sensorOrientation == 0 || sensorOrientation == 180)
                    exchange = true

            else ->
                LogUtil.logInfo("DisplayRotation 无效")
        }
        LogUtil.logInfo("displayRotation:${displayRotation},sensorOrientation:${sensorOrientation}")
        return exchange
    }

    /**
     * 根据提供的参数值返回与指定宽高相等或最接近的尺寸
     * @param targetWidth   目标宽度
     * @param targetHeight  目标高度
     * @param sizeList      支持的Size列表
     * @return  返回与指定宽高相等或最接近的尺寸
     */
    private fun getBestSize(targetWidth: Int, targetHeight: Int, sizeList: Array<Size>): Size {
        var tempSize = sizeList[0]
        var tempDiffWidth = targetWidth
        for (size in sizeList) {
            //宽高比 == 目标值宽高比
            if (size.width == size.height * targetWidth / targetHeight) {
                if (size.width == targetWidth && size.height == targetHeight) {
                    //预览尺寸正好一样，则直接返回
                    return size
                } else if (size.width < targetWidth) {
                    //以宽为参照物，查找相近的尺寸
                    val diffWidth = targetWidth - size.width
                    if (diffWidth < tempDiffWidth) {
                        tempDiffWidth = diffWidth
                        tempSize = size
                    }
                }
            }
        }

        return tempSize
    }

}